﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Xml.Linq;

namespace GDFlacTool.Common
{
    public class ArtistGenreHelper
    {
        private XDocument _xDocument;
        private readonly string _artistGenreXmlPath = string.Empty;
        //private readonly List<ArtistGenre> _artistGenreCollection = new List<ArtistGenre>();

        public ArtistGenreHelper(string path)
        {
            _artistGenreXmlPath = path;
        }

        public bool ArtistGenreXmlExist
        {
            get { return File.Exists(_artistGenreXmlPath); }
        }

        public XDocument GetArtistGenreXmlDocument
        {
            get { return _xDocument; }
        }

        public string GetArtistGenreXmlPath
        {
            get
            {
                if(File.Exists(_artistGenreXmlPath))
                    return _artistGenreXmlPath;
                return string.Empty;
            }
        }

        public int Count
        {
            get
            {
                if (_xDocument != null)
                {
                    return _xDocument.Descendants("ArtistGenre").Count();
                }
                    
                return 0;
            }
        }

        public bool IsArtistInList(string artist)
        {
            return (from elem in _xDocument.Descendants("ArtistGenre")
                           let element = elem.Element("Artist")
                           where element != null && element.Value == artist
                           select elem).Any();
        }

        public bool IsGenreInList(string genre)
        {
            return (from elem in _xDocument.Descendants("ArtistGenre")
                    let element = elem.Element("Genre")
                    where element != null && element.Value == genre
                    select elem).Any();
        }

        public bool ArtistGenreExists(string artist, string genre)
        {
            //return _artistGenreCollection.Find(p => p.Equals(artistGenre)) == null;

            return (from elem in _xDocument.Descendants("ArtistGenre")
                    let element1 = elem.Element("Genre")
                    let element2 = elem.Element("Artist")
                    where element1 != null && element1.Value == genre && element2 != null && element2.Value == artist
                    select elem).Any();
        }

        public string GetGenreFromArtist(string artist)
        {
            if (string.IsNullOrEmpty(artist))
            {
                return string.Empty;
            }

            XElement xElement = (from elem in _xDocument.Descendants("ArtistGenre")
                                 let element = elem.Element("Artist")
                                 where element != null && element.Value == artist
                                 select elem).SingleOrDefault();

            if (xElement != null)
            {
                var element = xElement.Element("Genre");
                if (element != null)
                {
                    return element.Value;
                }
                    return string.Empty;
            }

            return string.Empty;
        }

        public StringCollection GetAllArtists()
        {
            var list = new StringCollection();

            if (_xDocument != null)
            {
                var xElements = _xDocument.Descendants("ArtistGenre").ToList();

                if (xElements.Any())
                {
                    foreach (var xElement in xElements)
                    {
                        XElement artist = xElement.Element("Artist");

                        if (artist != null)
                        {
                            list.Add(artist.Value);
                        }
                    }

                    return list;
                }

                return null;
            }

            return null;
        }

        public List<string> GetAllGenres()
        {
            var list = new List<string>();

            if (_xDocument != null)
            {
                var xElements = _xDocument.Descendants("ArtistGenre").ToList();

                if (xElements.Any())
                {
                    list.AddRange(from xElement in xElements select xElement.Element("Genre") into genre where genre != null select genre.Value);

                    return list;
                }

                return null;
            }

            return null;
        }

        public bool NewArtistGenreXml(out string message)
        {
            message = string.Empty;
            var xDeclaration = new XDeclaration("1.0", "utf-8", "");
            var xCreatedAttribute = new XAttribute("created", string.Format("{0} {1}", DateTime.Now.ToShortDateString(), DateTime.Now.ToLongTimeString()) );
            var xName = new XAttribute("Name", "GDFlacTool Artist - Genre List");
            var xElement = new XElement("ArtistGenreList", xCreatedAttribute, xName);

            try
            {
                _xDocument = new XDocument(xDeclaration, xElement);
                _xDocument.Save(_artistGenreXmlPath, SaveOptions.OmitDuplicateNamespaces);
            }
            catch (Exception ex)
            {
                message = ex.Message;
                return false;
            }

            return true;
        }

        public bool OpenArtistGenreXml(out string message)
        {
            message = string.Empty;

            if (string.IsNullOrEmpty(_artistGenreXmlPath) || !File.Exists(_artistGenreXmlPath))
            {
                message = "Could not open the xml file!";
                return false;
            }

            try
            {
                _xDocument = XDocument.Load(_artistGenreXmlPath);

                //_xElements = _xDocument.Descendants("ArtistGenre").ToList();
            }
            catch (Exception ex)
            {
                message = ex.Message;
                return false;
            }

            return true;
        }

        public bool SaveArtistGenreXml(out string message)
        {
            message = string.Empty;

            try
            {
                _xDocument.Save(_artistGenreXmlPath, SaveOptions.OmitDuplicateNamespaces);
            }
            catch (Exception ex)
            {
                message = ex.Message;
                return false;
            }

            return true;
        }

        public bool AddArtistGenre(string artist, string genre, out string message)
        {
            message = string.Empty;

            if (!string.IsNullOrEmpty(artist) && !string.IsNullOrEmpty(genre))
            {
                var xAdded = new XAttribute("added", string.Format("{0} {1}", DateTime.Now.ToShortDateString(), DateTime.Now.ToLongTimeString()));
                var xNumber = new XAttribute("id", string.Format("{0:0000#}", FindNextId()));
                var xElementArtistGenre = new XElement("ArtistGenre", xAdded, xNumber);
                var xElementArtist = new XElement("Artist");
                var xElementGenre = new XElement("Genre");
                
                xElementArtist.Value = artist;
                xElementGenre.Value = genre;
                xElementArtistGenre.Add(xElementArtist, xElementGenre);

                try
                {
                    bool exists = (from elem in _xDocument.Descendants("ArtistGenre")
                               let element = elem.Element("Artist")
                               where element != null && element.Value == artist
                               select elem).Any();

                    if (!exists)
                    {
                        XElement xElement = _xDocument.Descendants("ArtistGenreList").Single();

                        xElement.Add(xElementArtistGenre);

                        _xDocument.Save(_artistGenreXmlPath, SaveOptions.OmitDuplicateNamespaces);

                        //_xElements = _xDocument.Descendants("ArtistGenre").ToList();
                    }
                }
                catch (Exception ex)
                {
                    message = ex.Message;
                    return false;
                }

                return true;
            }

            message = "Could not add the artist, the artist or genre tag is not set!";
            return false;
        }

        public bool RemoveAtristGenre(string artist, out string message)
        {
            message = string.Empty;

            if (!string.IsNullOrEmpty(artist))
            {
                try
                {
                    XElement xElement = (from elem in _xDocument.Descendants("ArtistGenre")
                                         let element = elem.Element("Artist")
                                         where element != null && element.Value == artist
                                         select elem).Single();

                    if (xElement != null)
                    {
                        xElement.Remove();

                        _xDocument.Save(_artistGenreXmlPath, SaveOptions.OmitDuplicateNamespaces);
                    }
                }
                catch (Exception ex)
                {
                    message = ex.Message;
                    return false;
                }

                return true;
            }

            message = "Could not remove the artist, the artist tag is not set!";
            return false;
        }

        public bool UpdateAllArtistGenre(List<ArtistGenre> list)
        {
            if (_xDocument != null && list.Count > 0)
            {
                var xElements = _xDocument.Descendants("ArtistGenre").ToList();

                if (xElements.Any())
                {
                    foreach (var row in list)
                    {
                        string id = string.Format("{0:0000#}", row.Id);

                        //Edit node
                        if (row.Status == ArtistGenreStatus.Edit)
                        {
                            XElement xElement = (from elem in _xDocument.Descendants("ArtistGenre")
                                                 let atrib = elem.Attribute("id")
                                                 where atrib != null && atrib.Value == id 
                                                 select elem).Single();

                            if (xElement != null)
                            {
                                var elementArtist = xElement.Element("Artist");
                                if (elementArtist != null) elementArtist.Value = row.Artist;

                                var elementGenre = xElement.Element("Genre");
                                if (elementGenre != null) elementGenre.Value = row.Genre;
                            }
                        }
                        //Delete node
                        if (row.Status == ArtistGenreStatus.Remove)
                        {
                            XElement xElement = (from elem in _xDocument.Descendants("ArtistGenre")
                                                 let atrib = elem.Attribute("id")
                                                 where atrib != null && atrib.Value == id
                                                 select elem).Single();

                            if (xElement != null)
                            {
                                xElement.Remove();
                            }
                        }
                        //New node
                        if (row.Status == ArtistGenreStatus.New)
                        {
                            var xAdded = new XAttribute("added", string.Format("{0} {1}", DateTime.Now.ToShortDateString(), DateTime.Now.ToLongTimeString()));
                            var xNumber = new XAttribute("id", string.Format("{0:0000#}", row.Id));
                            var xElementArtistGenre = new XElement("ArtistGenre", xAdded, xNumber);
                            var xElementArtist = new XElement("Artist");
                            var xElementGenre = new XElement("Genre");

                            xElementArtist.Value = row.Artist;
                            xElementGenre.Value = row.Genre;
                            xElementArtistGenre.Add(xElementArtist, xElementGenre);

                            XElement xElement = _xDocument.Descendants("ArtistGenreList").Single();

                            xElement.Add(xElementArtistGenre);

                            //_xDocument.Save(_artistGenreXmlPath, SaveOptions.OmitDuplicateNamespaces);
                        }
                    }

                    _xDocument.Save(_artistGenreXmlPath, SaveOptions.OmitDuplicateNamespaces);
                }
            }

            return false;
        }

        public List<ArtistGenre> GetAllArtistGenre()
        {
            var list = new List<ArtistGenre>();

            if (_xDocument != null)
            {
                var xElements = _xDocument.Descendants("ArtistGenre").ToList();

                if (xElements.Any())
                {
                    foreach (var xElement in xElements)
                    {
                        var artistGenre = new ArtistGenre();

                        XAttribute added = xElement.Attribute("added");
                        XAttribute id = xElement.Attribute("id");
                        XElement artist = xElement.Element("Artist");
                        XElement genre = xElement.Element("Genre");

                        if (added != null)
                        {
                            DateTime dt = DateTime.Parse(added.Value);
                            artistGenre.Date = string.Format("{0} {1}", dt.ToShortDateString(), dt.ToLongTimeString());
                        }

                        if (id != null)
                        {
                            int i = int.Parse(id.Value);
                            artistGenre.Id = i;
                        }

                        if (artist != null)
                        {
                            artistGenre.Artist = artist.Value;
                        }

                        if (genre != null)
                        {
                            artistGenre.Genre = genre.Value;
                        }

                        list.Add(artistGenre);
                    }

                    return list;
                }

                return null;
            }

            return null;
        }

        public int FindNextId()
        {
            int counter = 0;

            List<XElement> xElements = _xDocument.Descendants("ArtistGenre").ToList();
                                        
            if (xElements.Any())
            {
                foreach (var xElement in xElements)
                {
                    int number;

                    XAttribute attr = (from attrib in xElement.Attributes("id")
                                       let attribut = "id"
                                       where attribut != null
                                       select attrib).Single();

                    string value = attr.Value;

                    if (int.TryParse(value, out number))
                    {
                        if (number > counter)
                        {
                            counter = number;
                        }
                    }
                }
            }           

            return counter + 1;
        }
    }

    public enum ArtistGenreStatus
    {
        Archive = 0,
        New = 1,
        Edit = 2,
        Remove = 3
    }

    public class ArtistGenre
    {
        private string _genre = "<Empty>";
        private string _artist = "<Empty>";
        private int _id;
        private string _date = "0001-01-01 00:00:00";
        private ArtistGenreStatus _status = ArtistGenreStatus.Archive;

        public string Genre
        {
            get { return _genre; }
            set { _genre = value; }
        }

        public string Artist
        {
            get { return _artist; }
            set { _artist = value; }
        }

        public int Id
        {
            get { return _id; }
            set { _id = value; }
        }

        public string Date
        {
            get { return _date; }
            set { _date = value; }
        }

        public ArtistGenreStatus Status
        {
            get { return _status; }
            set { _status = value; }
        }
    }
}
